/* 算法实现模板 */
import AlgorithmBase from "./AlgorithmBase";
import Vector, {toVector} from "../../common/Vector";
import { showMathStringInTable } from "../../visual/Table2D";
import { drawDecisionTree } from "../../visual/Tree2D";
import Figure2D from "../../visual/Figure2D";

import DefautValue from "./input/TreePruning.input.json";

class TreePruning extends AlgorithmBase{
  constructor(){
    super('TreePruning');
    
    const figure = new Figure2D(600, 450);
    figure.xMax = 10.0;
    figure.xMin = 0.0;
    figure.yMax = 10.0;
    figure.yMin = 0.0;
    figure.gridX = 12;
    figure.gridY = 12;
    
    this.figure = figure;
    this.trainLog = [];
    this.len = 0
    this.Ct = 0 
    this.max = 0
    this.flag = 0
    this.node_name_ = ''
    this.cmd =''
    this.width = 0

  }

  init()
  {
    this.loadModel(DefautValue);

    if(this.domFigure) {
      this.domFigure.innerHTML = '';
      this.domFigure.appendChild(this.figure.domElement);
    }
    this.print();
  }

  loadModel(json){
    if(json.TrainDS){
      if(json.TrainDS instanceof Array){
        this.trainDSList = [];
        for(let i = 0, il = json.TrainDS.length; i < il; i++){
          const dat = json.TrainDS[i];
          dat.x = toVector(dat.x);
          this.trainDSList.push(dat);
        }
        this.trainDS = this.trainDSList[0];
      }
      else{
        let trainDS = json.TrainDS;
        this.trainDS = trainDS;
      }
    }
    
    if(json.Model){
      const model   = json.Model;
      model.w = new Vector(model.w, true);
      this.model = model;
    }
  }

  displayModel(){
    const dataSet = this.trainDS.dataSet
    const label = this.trainDS.label
    var trainLog = []

    var label_=['ID']
    for(var i=0;i<label.length;i++){
      label_.push("\\textbf{"+label[i]+"}")
    }

    trainLog.push(label_)

    for(var j=0;j<dataSet.length;j++){
      var data_=[]
      data_.push((j+1).toString())
      for(var k=0;k<dataSet[j].length;k++){
        data_.push("\\textbf{"+dataSet[j][k]+"}")
      }
      trainLog.push(data_)
    }
 
    if(this.domTest) this.domTest.innerHTML = '';
    showMathStringInTable(this.domTest, trainLog);
  }

  // 显示训练数据集
  displayTrain(){
    const tree = this.trainDS.tree

    if(this.domTrain) this.domTrain.innerHTML = '';
    drawDecisionTree(this.domTrain, tree);
  }

  get_depth(tree){
    let that = this
    that.max = 1
    that.width = 0
    var rev = that.get_depth_(tree, that.max)
    return rev
  }
  
  get_depth_(tree, depth=1){
    let that = this
    for(var key in tree){
      var d = depth
      if((Object.keys(tree).length == 1))
      {
        if(d>that.max){
          that.max=d
        }
        that.width++
      }
      else if(key!='node'){
        d++
        that.get_depth_(tree[key], d)
      }
    }
    return {"a":that.max, "b":that.width}
  }

  tree_prun_(tree, data=[], label=[], depth=1, tree_pre=''){
    var str = tree_pre
    let that = this
    for(var key in tree){
      var d = depth
      if(key != 'node'){
        if(Object.keys(tree[key]).length == 1)
        {
          if((d+1) == that.max && that.flag==0){
            that.flag ++ 
            that.node_name = tree['node']
            var N = []
            var node_name = []
            for(var key_ in tree){
              if(key_!='node'){
                var index=0
                for(var i=0;i<label.length;i++){
                  if(tree['node']==label[i]){
                    index=i
                    break
                  }
                }
                var data_=[]
                for(var j=0;j<data.length;j++){
                  if(data[j][index]==key_){
                    data_.push(data[j])
                  }
                }
                N.push(data_.length)
                node_name.push(tree[key_]['node'])
              }
            }
  
            var max_index = 0
            var max_n = 0
            for(var k=0;k<N.length;k++){
              if(N[k]>max_n){
                max_n = N[k]
                max_index = k
              }
            }
            that.node_name_ = node_name[max_index] //aaaaaaaaaaaaaaaaaaaaa
            that.cmd = str
            break
          }
          else {
            that.node_name_ = '' //aaaaaaaaaaaaaaaaaaaaa
            that.cmd = ''
          }
        }
        else{
          d++
          var index=0
          for(var i=0;i<label.length;i++){
            if(tree['node']==label[i]){
              index=i
              break
            }
          }
          var data_=[]
          for(var j=0;j<data.length;j++){
            if(data[j][index]==key){
              data_.push(data[j])
            }
          }
          if(str.length==0){
            str += "new_tree['" + key + "']" 
          }
          else{
            str+="['" + key + "']" 
          }
          that.tree_prun_(tree[key], data_, label, d, str)
        }
      }
    }
    return {"a" : that.cmd, "b": that.node_name_}
  }

  purn(tree,data=[],label=[],f=0){
    let that = this
    var new_tree = Object.assign({}, tree);
    this.max = that.get_depth(tree).a
    this.flag = f
    this.node_name_ = ''
    this.node_name = ''
    this.cmd =''
    var rev = that.tree_prun_(tree, data, label)
    if(rev.a.length>0 && rev.b.length>0)
    {
      var cmd_ = "delete " + rev.a 
      eval(cmd_)
      var str_ = rev.a + " = {'node' : '" + rev.b + "'}" 
      eval(str_)
    }
    return {"a" : new_tree, "b": that.node_name}
  }

  cal_cost(tree, data, label){
    let that = this
    that.len = 0
    that.Ct = 0
    var Cat = that.cal_cost_(tree , data, label)
    return Cat
  }
  
  cal_cost_(tree, data, label){
    let that = this
    var node = tree['node']
    for(var key in tree){
      if(Object.keys(tree).length == 1)
      {
        this.len++
        //找到有几类
        var know = []
        know.push(data[0])
        for(var i=1;i<data.length;i++){
          var unknown = 0
          for(var j=0;j<know.length;j++){
            if(data[i].toString()==know[j].toString()){
              unknown = 0
              break
            }
            else{
              unknown = 1
            }
          }
          if(unknown==1){
            know.push(data[i])
          }
        }
        //计算损失函数
        var know_index=[]
        for(var k=0;k<know.length;k++)
        {
          know_index[k]=0
        }
        for(var x=0;x<know_index.length;x++){
          for(var y=0;y<data.length;y++){
            if(data[y].toString()==know[x].toString())
            {
              know_index[x]++
            }
          }
        }
        var Ctk=0
        for(var z=0;z<know_index.length;z++){
          Ctk+=know_index[z]*(Math.LOG2E*Math.log(know_index[z]/data.length))
        }
        that.Ct += -Ctk
        break
      }
      if(key!='node'){
        var data_ = []
        var index = 0
        for(var i=0;i<label.length;i++){
          if(label[i]==node){
            index = i
            break
          }
        }
        for(var j=0;j<data.length;j++){
          if(data[j][index]==key){
            data_.push(data[j])
          }
        }
        that.cal_cost_(tree[key], data_, label)
      }
    }
    return that.Ct + 1*that.len
  }

  train(){
    const dataSet = this.trainDS.dataSet
    const label = this.trainDS.label

    var tree = this.trainDS.tree
    
    let that = this

    var trainLog = [];
    var count = 0 
    trainLog.push(["\\text{迭代次数}","待剪枝节点","\\text{剪枝前}C_a (T_b)","\\text{剪枝后}C_a (T_a)","\\text{是否剪枝}"])

    while(that.get_depth(tree).a>2){
      var rev = this.get_depth(tree)
      var Cta = that.cal_cost(tree, dataSet, label)
      var flag = 0
      for(var i=0;i<rev.b;i++){
        var new_ = that.purn(tree,dataSet,label,-i)
        if(JSON.stringify(tree) === JSON.stringify(new_.a))
        {
          continue
        }
        var Ctb = that.cal_cost(new_.a,dataSet,label)
        count++
        if(Ctb <= Cta){
          tree = Object.assign({}, new_.a);
          flag = 1
          trainLog.push([`${count}`, `${new_.b}`, `${Cta}`, `${Ctb}`, "\\text{是}"])
          break
        }
        else{
          trainLog.push([`${count}`, `${new_.b}`, `${Cta}`, `${Ctb}`, "\\text{否}"])
        }
      }
      if(!flag){
        break
      }
    }
    if(this.domModel) this.domModel.innerHTML = '';
    drawDecisionTree(this.domModel, tree);
    if(this.domTable) this.domTable.innerHTML = '';
    showMathStringInTable(this.domTable, trainLog);
  }

  print(){
    this.figure.clearAll();
    this.displayModel();
    this.displayTrain();
  }
}


export default TreePruning;